/*
	This file is part of Vajolet.

    Vajolet is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Vajolet is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Vajolet.  If not, see <http://www.gnu.org/licenses/>
*/

#include <iostream>
#include <string.h>
#include <iomanip>
#include "vajolet.h"
#include "board.h"
#include "data.h"
#include "bitops.h"
#include "hash.h"
#include "data.h"
#include "movegen.h"
#include <assert.h>
#include <iostream>

const char* Board::PIECENAMES[lastBitboard]= {"  ","K ","Q ","R ","B ","N ","P ","  ","  ","K*","Q*","R*","B*","N*","P*","  "};
const char* Board::PIECENAMESFEN[lastBitboard]= {" ","K","Q","R","B","N","P"," "," ","k","q","r","b","n","p"," "};

/*const */score Board::PIECESCORE[lastBitboard]={0,20000,3963,1782,1218,1110,300,0,  0,-20000,-3963,-1782,-1218,-1110,-300,0 };

const char* Board::PIECECHARS[lastBitboard]= {" ","K","Q","R","B","N","P"," "," ","K","Q","R","B","N","P"," "};

struct HashKeys Board::KEY;

Board board;
void Board::displayFen(){
	int file,rank;
	int emptyFiles=0;
	for (rank = 7; rank >= 0; rank--)
	{
		emptyFiles=0;
		for (file = 0; file <= 7; file++)
		{
			if(PIECENAMESFEN[square[BOARDINDEX[file][rank]]]!=std::string(" ")){
				if(emptyFiles!=0){
					std::cout<<emptyFiles;
				}
				emptyFiles=0;
				std::cout << PIECENAMESFEN[square[BOARDINDEX[file][rank]]];
			}else{
				emptyFiles++;
			}
			
			
		}
		if(emptyFiles!=0){
			std::cout<<emptyFiles;
		}
		if(rank!=0){
			std::cout<<'/';
		}
	}
	std::cout<<' ';
	if(this->nextMove){
		std::cout<<'b';
	}else{
		std::cout<<'w';
	}
	std::cout<<' ';

	if(castleRights&wCastleOO){
		std::cout<<"K";
	}
	if(castleRights&wCastleOOO){
		std::cout<<"Q";
	}
	if(castleRights&bCastleOO){
		std::cout<<"k";
	}
	if(castleRights&bCastleOOO){
		std::cout<<"q";
	}
	if(castleRights==0){
		std::cout<<"-";
	}
	std::cout<<' ';
	if(epSquare!=0){
		std::cout<<char('a'+FILES[epSquare]);
		std::cout<<char('1'+RANKS[epSquare]);
	}else{
		std::cout<<'-';
	}
	std::cout<<' ';
	std::cout<<fiftyMoveCnt;
	std::cout<<" 1"<<std::endl;
	
}
void Board::display()
{
	displayFen();
	int rank, file;
	std::cout << std::endl;
	{
		if (!viewRotated)
		{
			for (rank = 7; rank >= 0; rank--)
			{
				std::cout << "    +---+---+---+---+---+---+---+---+" << std::endl;
				std::cout << std::setw(3) << rank+1 <<  " |";
				for (file = 0; file <= 7; file++)
				{
					std::cout << " " << PIECENAMES[square[BOARDINDEX[file][rank]]] << "|";
				}
				std::cout << std::endl;
			}
			std::cout << "    +---+---+---+---+---+---+---+---+" << std::endl;
			std::cout << "      a   b   c   d   e   f   g   h" << std::endl << std::endl;
		}
		else
		{
			std::cout << "      h   g   f   e   d   c   b   a" << std::endl;
			for (rank = 0; rank <= 7; rank++)
			{
				std::cout << "    +---+---+---+---+---+---+---+---+" << std::endl;
				std::cout << "    |";
				for (file = 7; file >= 0; file--)
				{
					std::cout << " " << PIECENAMES[square[BOARDINDEX[file][rank]]] << "|";
				}
				std::cout << std::setw(3) << rank+1 << std::endl;
			}
			std::cout << "    +---+---+---+---+---+---+---+---+" << std::endl << std::endl;
		}
	}
	std::cout <<"material " <<Material <<std::endl;
	std::cout <<"whitepawn " << bitCnt(boards[whitePawns])*PIECESCORE[whitePawns]<<std::endl;
	std::cout <<"blackpawn " << bitCnt(boards[blackPawns])*PIECESCORE[whitePawns]<<std::endl;
	std::cout <<"whitemat " << totalWhitePieces<<std::endl;
	std::cout <<"blackmat " << totalBlackPieces<<std::endl;
	std::cout <<(nextMove?"BLACK TO MOVE":"WHITE TO MOVE") <<std::endl;
	std::cout <<"50 move counter "<<fiftyMoveCnt<<std::endl;
	std::cout <<"castleRights " <<castleRights<<std::endl;
	//displayBitmap(BITSET[epSquare]); 
	//score s=eval<false>();
	//std::cout <<"eval: "<<s<<std::endl;
	//std::cout<<"game phase: "<<(unsigned int)getGamePhase()<<std::endl;

	return;
}

void Board::init(void){
	//KEY.init();
	viewRotated = false;
	epSquare=0;
	//kingInCheck=dontKnow;

	for (int i = 0; i < 64; i++) square[i] = empty;
	gameLineIndex=0;

	square[E1] = whiteKing;
	square[D1] = whiteQueens;
	square[A1] = whiteRooks;
	square[H1] = whiteRooks;
	square[B1] = whiteKnights;
	square[G1] = whiteKnights;
	square[C1] = whiteBishops;
	square[F1] = whiteBishops;
	square[A2] = whitePawns;
	square[B2] = whitePawns;
	square[C2] = whitePawns;
	square[D2] = whitePawns;
	square[E2] = whitePawns;
	square[F2] = whitePawns;
	square[G2] = whitePawns;
	square[H2] = whitePawns;
	
	square[E8] = blackKing;
	square[D8] = blackQueens;
	square[A8] = blackRooks;
	square[H8] = blackRooks;
	square[B8] = blackKnights;
	square[G8] = blackKnights;
	square[C8] = blackBishops;
	square[F8] = blackBishops;
	square[A7] = blackPawns;
	square[B7] = blackPawns;
	square[C7] = blackPawns;
	square[D7] = blackPawns;
	square[E7] = blackPawns;
	square[F7] = blackPawns;
	square[G7] = blackPawns;
	square[H7] = blackPawns;



	initFromSquares( white, 0, (eCastle)(wCastleOO + wCastleOOO + bCastleOO + bCastleOOO), 0);

	assert(!checkBoard());
}


void Board::initFromSquares( eNextMove next, int fiftyM, eCastle castle, int epSq)
{
	// sets up the board variables according to the information found in
	// the input[64] array
	// All board & game initializations are done through this function (including readfen and setup).
 
	int i;
	

	// bitboards
	for(int i=0;i<lastBitboard;i++){
		boards[i]=0;
	}

	

	// populate the 12 bitboard:
	for (i = 0; i < 64; i++)
	{
		//square[i] = input[i];
		boards[square[i]] |= BITSET[i];
	}
	
	for( i=whiteKing;i<=whitePawns;i++){
		boards[whitePieces]|=boards[i];
	}
	for( i=blackKing;i<=blackPawns;i++){
		boards[blackPieces]|=boards[i];
	}
	
 
	
	boards[occupiedSquares] = boards[whitePieces] | boards[blackPieces];
 
	nextMove = next;

	castleRights=castle;
	
	epSquare = epSq;
	fiftyMoveCnt = fiftyM;

	
 
	totalWhitePieces=0;
	for( i=whiteQueens;i<=whiteKnights;i++){
		totalWhitePieces+=bitCnt(boards[i])*PIECESCORE[i];
	}
	totalBlackPieces=0;
	for( i=blackQueens;i<=blackKnights;i++){
		totalBlackPieces-=bitCnt(boards[i])*PIECESCORE[i];
	}

	//totalWhitePawns = bitCnt(boards[whitePawns])*PIECESCORE[whitePawns];
	//totalBlackPawns = (bitCnt(boards[blackPawns])*-PIECESCORE[blackPawns]);
	hashKey=calcHash();
	pawnKey=calcPawnsHash();
	

	
	Material  = bitCnt(boards[whitePawns])*PIECESCORE[whitePawns] + totalWhitePieces - bitCnt(boards[blackPawns])*PIECESCORE[whitePawns] - totalBlackPieces;

	for(int i=0;i<lastBitboard;i++){
		pieceCount[i]=bitCnt(boards[i]);
	}

	materialKey=calcMatHash();
	getPinnedPieces();
 
	assert(!checkBoard());

	return;
}

U64 Board::calcHash(){
	U64 hash=0;
	int i;
	for (i = 0; i < 64; i++)
	{
		if(square[i]!=empty){
			hash ^=KEY.keys[i][square[i]];
		}
	}

	if (castleRights & wCastleOO){hash ^= KEY.castlingRight[wCastleOO];}
	if (castleRights & wCastleOOO){hash ^= KEY.castlingRight[wCastleOOO];}
	if (castleRights & bCastleOO){hash ^= KEY.castlingRight[bCastleOO];}
	if (castleRights & bCastleOOO){hash ^= KEY.castlingRight[bCastleOOO];}
	if (nextMove){ hash ^= KEY.side;}
	if (epSquare){ hash ^= KEY.ep[epSquare];}
	
	return hash;
}

U64 Board::calcMatHash(){
	U64 hash=0;
	int i;
	for (i = 0; i < lastBitboard; i++)
	{
		if(i!=occupiedSquares && i!=whitePieces && i!=emptyBitmap && i!=blackPieces){
			//std::cout<<(int)i<<" "<<(int)pieceCount[i]<<std::endl;
			hash ^=KEY.keys[pieceCount[i]][i];
		}
	}
	
	return hash;
}


U64 Board::calcPawnsHash(){
	U64 hash=0;
	int i;
	for (i = 0; i < 64; i++)
	{
		if(square[i]==whitePawns || square[i]==blackPawns){
			hash ^=KEY.keys[i][square[i]];
		}
	}
	return hash;
}


int Board::checkBoard(void){
	bitMap temp;
	int n;
	// check che le bitboards siano congruenti con le square 
	for(int i=A1; i<H8;i++){
		if (square[i]!= empty &&(boards[ square[i]] & BITSET[i]) ==0){
			std::cout<<"errore nella casella " <<i<<std::endl;
			std::cin>>n;
			return -1;
		}
	}

	//check che le squares siano congruenti con le bitboard
	for(int i=whiteKing;i<=blackPawns;i++){
		if(i==whitePieces || i==emptyBitmap){
			continue;
		}
		temp= boards[i];
		while(temp){
			int n=firstOne(temp);

			if(square[n] != i){

				displayBitmap(boards[i]);
				std::cout<<"errore 2"<<std::endl;
				std::cout<<i<<std::endl;
				int gli=gameLineIndex;
				init();
				for(int i=0;i<=gli-2;i++){
					gameLine[i].move.display();
					if(gameLine[i].move.packed==0){
						makeNullMove();
					}else{
						makeMove(gameLine[i].move);
					}
					std::cout<<std::endl;
				}
				display();
				gameLine[gameLineIndex].move.display();
				std::cout<<std::endl;
				Movegen mg(0,0,-LARGE_NUMBER);
				if(mg.isValidMove(gameLine[gameLineIndex].move)){
					std::cout<<"mossa Valida";
				}
				std::cin>>n;
				return -1;
			}

			temp ^= BITSET[n];
		}
	}
	temp =0;
	for(int i=whiteKing;i<=whitePawns;i++){
		temp|=boards[i];
	}
	if(temp!= boards[whitePieces]){
		displayBitmap(temp);
		displayBitmap(boards[whitePieces]);
		std::cout<<"errore whitePieces bitmap"<< std::endl;
		std::cin>>n;
		return -1;
	}
	
	temp =0;
	for(int i=blackKing;i<=blackPawns;i++){
		temp|=boards[i];
	}
	if(temp!= boards[blackPieces]){
		displayBitmap(temp);
		displayBitmap(boards[blackPieces]);
		for(int i=0;i<gameLineIndex;i++){
			gameLine[i].move.display();
			std::cout<<std::endl;
		}
		display();
		std::cout<<"errore blackPieces bitmap"<< std::endl;
		std::cin>>n;
		return -1;
	}

	temp =0;
	for(int i=whiteKing;i<=whitePawns;i++){
		temp|=boards[i];
	}
	for(int i=blackKing;i<=blackPawns;i++){
		temp|=boards[i];
	}
	if(temp!= boards[occupiedSquares]){
		displayBitmap(temp);
		displayBitmap(boards[whitePieces]);
		std::cout<<"errore occupied squares bitmap"<< std::endl;
		std::cin>>n;
		return -1;
	}


	if(calcHash() != hashKey){
		std::cout<< "wrong HashKey"<<std::endl;
		for(int i=0;i<gameLineIndex;i++){
			gameLine[i].move.display();
			std::cout<<std::endl;
		}
		display();
		int foo;
		std::cin>>foo;

		return -1;
	}

	if(calcPawnsHash() != pawnKey){
		std::cout<< "wrong pawnKey"<<std::endl;
		display();
		int foo;
		std::cin>>foo;
		return -1;
	}

	if(calcMatHash() != materialKey){
		std::cout<< "wrong materialKey"<<std::endl;
		display();
		int foo;
		std::cin>>foo;
		return -1;
	}


	score ltotalWhitePieces=0;
	for(int  i=whiteQueens;i<=whiteKnights;i++){
		ltotalWhitePieces+=bitCnt(boards[i])*PIECESCORE[i];
	}
	if(ltotalWhitePieces != totalWhitePieces){
		std::cout<<"error totalWhitePieces"<<std::endl;
		std::cin>>n;
		return -1;
	}


	score ltotalBlackPieces=0;
	for(int  i=blackQueens;i<=blackKnights;i++){
		ltotalBlackPieces-=bitCnt(boards[i])*PIECESCORE[i];
	}

	if(ltotalBlackPieces != totalBlackPieces){
		std::cout<<"error totalBlackPieces"<<std::endl;
		std::cin>>n;
		return -1;
	}


	/*score ltotalWhitePawns = bitCnt(boards[whitePawns])*PIECESCORE[whitePawns];
	if(ltotalWhitePawns != totalWhitePawns){
		std::cout<<"error totalWhitePawns"<<std::endl;
		std::cin>>n;
		return -1;
	}

	score ltotalBlackPawns = (bitCnt(boards[blackPawns])*-PIECESCORE[blackPawns]);
	if(ltotalBlackPawns != totalBlackPawns){
		std::cout<<ltotalBlackPawns<<std::endl;
		std::cout<<totalBlackPawns<<std::endl;
		std::cout<<"error totalBlackPawns"<<std::endl;
		display();
		for(int i=0;i<gameLineIndex;i++){
			gameLine[i].move.display();
			std::cout<<std::endl;
		}
		std::cin>>n;
		return -1;
	}*/



	
	score lMaterial  = bitCnt(boards[whitePawns])*PIECESCORE[whitePawns] + ltotalWhitePieces - bitCnt(boards[blackPawns])*PIECESCORE[whitePawns] - ltotalBlackPieces;
	if(lMaterial != Material){
		for(int i=0;i<gameLineIndex;i++){
			gameLine[i].move.display();
			std::cout<<std::endl;
		}
		display();
		std::cout<<Material<<std::endl;
		std::cout<<lMaterial<<std::endl;
		std::cout<<"error Material"<<std::endl;
		std::cin>>n;
		return -1;
	}

	for(int i=1;i<lastBitboard;i++){
		if(i!=0 && i!=7 && i!=8 && i!=15 && pieceCount[i]!=bitCnt(boards[i]) ){
			display();
			std::cout<<(int)i<<std::endl;
			std::cout<<(int)pieceCount[i]<<std::endl;
			std::cout<<(int)bitCnt(boards[i])<<std::endl;
			std::cout<<"error piececount"<<std::endl;
			std::cin>>n;
			return -1;
		}
	}

	return 0;

}


void Board::unmakeMove(){

	//RECUPERO I DATI DALLA GAME LINE RECORD
	assert(gameLineIndex>=1);
	GamelineRecord & glr=gameLine[--gameLineIndex];
	bitboardIndex capturedPiece=(bitboardIndex)glr.capturedPiece;

	Move m;
	glr.move.copyTo(m);

	castleRights=glr.castleRights;
	epSquare=glr.epSquare;
	fiftyMoveCnt=glr.fiftyMoveCnt;
	hashKey=glr.hashKey;
	pawnKey=glr.pawnKey;
	materialKey=glr.materialKey;

	Material=glr.Material;
	//totalWhitePawns=glr.totalWhitePawns;
	//totalBlackPawns=glr.totalBlackPawns;
	totalWhitePieces=glr.totalWhitePieces; 
	totalBlackPieces=glr.totalBlackPieces;
	//kingInCheck=(Board::eKingInCheck)glr.kingInCheck;
	pinnedPieces=glr.pinnedPieces;
	hiddenCheckers=glr.hiddenCheckers;
	checkers=glr.checkers;


	// fine recupero dati

	bitMap mbTo =BITSET[m.to];
	bitMap mbfrom =BITSET[m.from];
	bitMap mbFromTo =mbfrom^mbTo;

	bitboardIndex piece = square[m.to];

	nextMove = (eNextMove)(black-nextMove);	//inverto il colore attivo

	boards[piece]^=mbFromTo;	//muovo il pezzo sulle bitboard
	square[m.from]=piece;			
	square[m.to]=empty;			
	
	if(capturedPiece){
		boards[capturedPiece]^=mbTo;	//muovo il pezzo sulle bitboard
		square[m.to]=capturedPiece;
		pieceCount[capturedPiece]++;
	}

	
	
	//AGGIORNAMENTO  whitepieces&blackPieces bitboard
	boards[whitePieces+nextMove]^=mbFromTo;
	if(capturedPiece)boards[blackPieces-nextMove]^=mbTo;// se e' una cattura aggiungo il pezzo avversario


	
	if(m.flags){
		if(m.flags == Move::fcastle){
			if(m.to== G1){
				boards[whiteRooks]^=(BITSET[F1]| BITSET[H1]);
				boards[whitePieces]^=(BITSET[F1] | BITSET[H1]);
				square[H1]=whiteRooks;
				square[F1]=empty;
			}else if(m.to== C1){
				boards[whiteRooks]^=(BITSET[D1]| BITSET[A1]);
				boards[whitePieces]^=(BITSET[D1]| BITSET[A1]);
				square[A1]=whiteRooks;
				square[D1]=empty;
			}else if(m.to== G8){
				boards[blackRooks]^=(BITSET[F8]| BITSET[H8]);
				boards[blackPieces]^=(BITSET[F8]| BITSET[H8]);
				square[H8]=blackRooks;
				square[F8]=empty;
			}else if(m.to== C8){
				boards[blackRooks]^=(BITSET[D8]| BITSET[A8]);
				boards[blackPieces]^=(BITSET[D8]| BITSET[A8]);
				square[A8]=blackRooks;
				square[D8]=empty;
			}
		}

		if(piece==Board::whitePawns)
		{
			if(m.flags == Move::fenpassant){
				square[m.to-8]=Board::blackPawns;
				boards[Board::blackPawns]^=BITSET[m.to-8];
				boards[Board::blackPieces]^=BITSET[m.to-8];
				pieceCount[blackPawns]++;
			}
		}

		if(piece==Board::blackPawns)
		{
			if(m.flags == Move::fenpassant){
				square[m.to+8]=Board::whitePawns;
				boards[Board::whitePawns]^=BITSET[m.to+8];
				boards[Board::whitePieces]^=BITSET[m.to+8];
				pieceCount[whitePawns]++;
			}
		}

		if(m.flags ==Move::fpromotion){
			boards[piece]^= BITSET[m.from];
			pieceCount[piece]--;
			boards[whitePawns+nextMove]^= BITSET[m.from];
			pieceCount[whitePawns+nextMove]++;
			square[m.from]=(bitboardIndex)(whitePawns+nextMove);
		}
	}
	
	boards[occupiedSquares]=boards[whitePieces]|boards[blackPieces];
#ifdef _DEBUG_MAKEMOVE
	checkBoard();
#endif
}




char Board::isAttacked(bitMap targetBitmap, const unsigned char fromSide, bitMap maskBit)
{

//  ===========================================================================
//  isAttacked is used mainly as a move legality test to see if targetBitmap is
//  attacked by white or black.
//  Returns true at the first attack found, and returns false if no attack is found.
//  It can be used for:
//  - check detection, and
//  - castling legality: test to see if the king passes through, or ends up on,
//  a square that is attacked
//  ===========================================================================
	bitMap tempTarget;
	bitMap slidingAttackers;
	int to;
	bitMap occupiedSquares=boards[Board::occupiedSquares]&~maskBit;

	int offset;
	if(fromSide){
		offset=Board::blackKing-Board::whiteKing;
	}
	else{
		offset=0;
	}
	
	tempTarget = targetBitmap;

	while (tempTarget)
	{
		to = firstOne(tempTarget);
			
		if (fromSide && (boards[whitePawns+offset]&~maskBit & WHITE_PAWN_ATTACKS[to])){ return true;}
		else if (!fromSide && (boards[whitePawns+offset]&~maskBit & BLACK_PAWN_ATTACKS[to])){ return true;}
		if (boards[whiteKnights+offset]&~maskBit & KNIGHT_ATTACKS[to]) return true;
		if (boards[whiteKing+offset]&~maskBit & KING_ATTACKS[to]) return true;
			
		// file / rank attacks
		slidingAttackers = (boards[whiteQueens+offset] |  boards[Board::whiteRooks+offset])&~maskBit;
		if ((slidingAttackers & RANKMASK[to]) && (RANKATTACKS(to) & slidingAttackers)) return true;
		if ((slidingAttackers & FILEMASK[to]) && (FILEATTACKS(to) & slidingAttackers)) return true;

		// diagonals
		slidingAttackers = (boards[whiteQueens+offset] |  boards[whiteBishops+offset])&~maskBit;
		
		if ((slidingAttackers & DIAGA8H1MASK[to])&& (SLIDEA8H1ATTACKS(to) & slidingAttackers)) return true;
		if ((slidingAttackers & DIAGA1H8MASK[to])&& (SLIDEA1H8ATTACKS(to) & slidingAttackers)) return true;

			
		tempTarget &= tempTarget-1;
	}
	
	return false;
}

char Board::isAttackedSingle(bitMap tempTarget, const unsigned char fromSide)
{

//  ===========================================================================
//  isAttacked is used mainly as a move legality test to see if targetBitmap is
//  attacked by white or black.
//  Returns true at the first attack found, and returns false if no attack is found.
//  It can be used for:
//  - check detection, and
//  - castling legality: test to see if the king passes through, or ends up on,
//  a square that is attacked
//  ===========================================================================
	//bitMap tempTarget;
	bitMap slidingAttackers;
	int to;
	bitMap occupiedSquares=boards[Board::occupiedSquares];

	int offset;
	if(fromSide){
		offset=Board::blackKing-Board::whiteKing;
	}
	else{
		offset=0;
	}
	
	//tempTarget = targetBitmap;

	//while (tempTarget)
	//{
		to = firstOne(tempTarget);
			
		if (fromSide && (boards[whitePawns+offset] & WHITE_PAWN_ATTACKS[to])){ return true;}
		else if (!fromSide && (boards[whitePawns+offset] & BLACK_PAWN_ATTACKS[to])){ return true;}
		if (boards[whiteKnights+offset] & KNIGHT_ATTACKS[to]) return true;
		if (boards[whiteKing+offset] & KING_ATTACKS[to]) return true;
			
		// file / rank attacks
		slidingAttackers = boards[whiteQueens+offset] |  boards[Board::whiteRooks+offset];
		if ((slidingAttackers & RANKMASK[to]) && (RANKATTACKS(to) & slidingAttackers)) return true;
		if ((slidingAttackers & FILEMASK[to]) && (FILEATTACKS(to) & slidingAttackers)) return true;

		// diagonals
		slidingAttackers = boards[whiteQueens+offset] |  boards[whiteBishops+offset];
		
		if ((slidingAttackers & DIAGA8H1MASK[to])&& (SLIDEA8H1ATTACKS(to) & slidingAttackers)) return true;
		if ((slidingAttackers & DIAGA1H8MASK[to])&& (SLIDEA1H8ATTACKS(to) & slidingAttackers)) return true;

			
		//tempTarget &= tempTarget-1;
	//}
	
	return false;
}

void Board::setupFen(const char *fen,const char *fencolor,const char *fencastling,const char *fenenpassant, int fenhalfmoveclock, int fenfullmovenumber)
{
       int i, file, rank, counter;
       int epsq;
       eNextMove lnext;
       eCastle lcastleRights;
	   //kingInCheck=dontKnow;

       for (i = 0; i < 64; i++)
       {
              square[i] = empty;
       }

       // loop over the FEN string characters, and populate board.square[]
       // i is used as index for the FEN string
       // counter is the index for board.square[], 0..63
       // file and rank relate to the position on the chess board, 1..8
       // There is no error/legality checking on the FEN string!!
       file = 0;
       rank = 7;
       i = 0;
       counter = 0;
       while ((counter < 64) && (fen[i] != '\0'))
       {
              // '1'  through '8':
              if (((int) fen[i] > '0') && ((int) fen[i] < '9'))
              {
                     file+= (int) fen[i] - '0';
                     counter+= (int) fen[i] - '0';
              }
              else
              //  other characters:
              {
                     switch (fen[i])
                     {
                           case '/':
                                  rank--;
                                  file = 0;
                                  break;

                           case 'P':
                                  square[BOARDINDEX[file][rank]] = whitePawns;
                                  file += 1;
                                  counter += 1;
                                  break;

                           case 'N':
                                  square[BOARDINDEX[file][rank]] = whiteKnights;
                                  file += 1;
                                  counter += 1;
                                  break;

                           case 'B':
                                  square[BOARDINDEX[file][rank]] = whiteBishops;
                                  file += 1;
                                  counter += 1;
                                  break;

                           case 'R':
                                  square[BOARDINDEX[file][rank]] = whiteRooks;
                                  file += 1;
                                  counter += 1;
                                  break;

                           case 'Q':
                                  square[BOARDINDEX[file][rank]] = whiteQueens;
                                  file += 1;
                                  counter += 1;
                                  break;

                           case 'K':
                                  square[BOARDINDEX[file][rank]] = whiteKing;
                                  file += 1;
                                  counter += 1;
                                  break;

                           case 'p':
                                  square[BOARDINDEX[file][rank]] = blackPawns;
                                  file += 1;
                                  counter += 1;
                                  break;

                           case 'n':
                                  square[BOARDINDEX[file][rank]] = blackKnights;
                                  file += 1;
                                  counter += 1;
                                  break;

                           case 'b':
                                  square[BOARDINDEX[file][rank]] = blackBishops;
                                  file += 1;
                                  counter += 1;
                                  break;

                           case 'r':
                                  square[BOARDINDEX[file][rank]] = blackRooks;
                                  file += 1;
                                  counter += 1;
                                  break;

                           case 'q':
                                  square[BOARDINDEX[file][rank]] = blackQueens;
                                  file += 1;
                                  counter += 1;
                                  break;

                           case 'k':
                                  square[BOARDINDEX[file][rank]] = blackKing;
                                  file += 1;
                                  counter += 1;
                                  break;

                           default:
                                  break;
                     }
              }
              i++;
       }
       lnext = white;
       if (fencolor[0] == 'b') lnext = black;

       lcastleRights = (eCastle)0;
       if (strstr(fencastling, "K")) lcastleRights = (eCastle)(lcastleRights|wCastleOO);
       if (strstr(fencastling, "Q")) lcastleRights = (eCastle)(lcastleRights|wCastleOOO);
       if (strstr(fencastling, "k")) lcastleRights = (eCastle)(lcastleRights|bCastleOO);
       if (strstr(fencastling, "q")) lcastleRights = (eCastle)(lcastleRights|bCastleOOO);
       if (strstr(fenenpassant, "-"))
       {
              epsq = 0;
       }
       else
       {
              // translate a square coordinate (as string) to int (eg 'e3' to 20):
              epsq = ((int) fenenpassant[0] - 'a') + 8 * ((int) fenenpassant[1] - '1') ;
			  //displayBitmap(BITSET[epsq]);
       }
       initFromSquares(lnext, fenhalfmoveclock, lcastleRights , epsq);

}

//show the last n moves of the line
void Board::showLine(unsigned int n){
	int index=gameLineIndex;
	for(unsigned int i=0;i<n;i++){
		unmakeMove();
	}
	for(unsigned int i=n;i>0;i--){
		gameLine[index-i].move.display();
		makeMove(gameLine[index-i].move);
		
	}
}

//get all the attackers to a square, it not take in account xray attacks or second line attackers
bitMap Board::getAttackersAndDefenders(int square)const {
	
	bitMap res=0;
	bitMap  attackBitmap;
	bitMap occupiedSquares;
	occupiedSquares =boards[Board::occupiedSquares];
	
	bitMap targets;
	

	targets=boards[blackQueens] | boards[whiteQueens] | boards[blackRooks] | boards[whiteRooks];
	if((RANKMASK[square] | FILEMASK[square] )& targets){
	// attacks along ranks/files (rooks & queens)
		attackBitmap = ROOKATTACKS(square);
		res |= (attackBitmap & targets);
	}
	
	targets=boards[blackQueens] | boards[whiteQueens] | boards[blackBishops] | boards[whiteBishops];
	if((DIAGA1H8MASK[square] |DIAGA8H1MASK[square])& targets){
	// attacks along diagonals (bishops & queens)
		attackBitmap = BISHOPATTACKS(square);
		res |= (attackBitmap & targets);
	}

	targets=(boards[blackKnights] | boards[whiteKnights] );//& ~pinnedPieces; funziona peggio se considero i pezzi inchiodati
	if(targets){
	// attacks from knights
		attackBitmap = KNIGHT_ATTACKS[square];
		res |= (attackBitmap & targets);
	}
	
	targets=boards[whitePawns];
	if(targets){
	// white pawn attacks (except en/passant)
		attackBitmap = BLACK_PAWN_ATTACKS[square];
		res |= (attackBitmap & targets);
	}

	targets=boards[blackPawns];
	if(targets){
	// black pawn attacks (except en/passant)
		attackBitmap = WHITE_PAWN_ATTACKS[square];
		res |= (attackBitmap & targets);
	}

	targets=boards[blackKing] | boards[whiteKing];
	if(targets){
	// king attacks 
		attackBitmap = KING_ATTACKS[square];
		res |= (attackBitmap & targets);
	}
	//res|=((boards[blackKing] | boards[whiteKing])&(KING_ATTACKS[square]));
	return res;
}

bitMap Board::getXraySliderAttackersAndDefenders(int square,bitMap removedPiece) const {

	bitMap res=0;
	bitMap  attackBitmap;
	bitMap occupiedSquares;
	occupiedSquares =boards[Board::occupiedSquares]& ~removedPiece;
	bitMap targets;

	targets=boards[blackQueens] | boards[whiteQueens] | boards[blackRooks] | boards[whiteRooks];
	
	if((RANKMASK[square] | FILEMASK[square] )& targets){
	// attacks along ranks/files (rooks & queens)
		attackBitmap = ROOKATTACKS(square);
		res |= (attackBitmap & targets);
	}
	targets=boards[blackQueens] | boards[whiteQueens] | boards[blackBishops] | boards[whiteBishops];
	if((DIAGA1H8MASK[square] |DIAGA8H1MASK[square])& targets){
	// attacks along diagonals (bishops & queens)
		attackBitmap = BISHOPATTACKS(square);
		res |= (attackBitmap & targets);
	}
	
	return res;
}


void Board::makeMove(Move m){
	assert(gameLineIndex<MAX_GAME_LENGTH);
	//Search::makeMoveCall++;

	bitMap mbTo =BITSET[m.to];
	bitMap mbfrom =BITSET[m.from];
	bitMap mbFromTo =mbfrom^mbTo;
	//salva dati importanti nella gameLineRecord
	
	GamelineRecord & glr=gameLine[gameLineIndex];
	Board::bitboardIndex capturedPiece = square[m.to];
	Board::bitboardIndex piece= square[m.from];

	//copyTo(glr.move);
	glr.move.packed=m.packed;
	glr.castleRights=castleRights;
	glr.epSquare=epSquare;
	glr.fiftyMoveCnt=fiftyMoveCnt;
	glr.hashKey=hashKey;
	glr.pawnKey=pawnKey;
	glr.materialKey=materialKey;
	glr.extended=false;

	glr.Material=Material;
	//glr.totalWhitePawns=totalWhitePawns;
	//glr.totalBlackPawns=totalBlackPawns;
	glr.totalWhitePieces=totalWhitePieces; 
	glr.totalBlackPieces=totalBlackPieces;
	glr.capturedPiece=capturedPiece;
	//glr.kingInCheck=kingInCheck;
	glr.pinnedPieces=pinnedPieces;
	glr.hiddenCheckers=hiddenCheckers;
	glr.checkers=checkers;



	//kingInCheck=Board::dontKnow;

	gameLineIndex++;

	/*if(gameLineIndex>19){
			std::cout<<"-----------------------------------------------------------------------------------------------------------"<<std::endl;
			std::cout<<gameLineIndex<<":";
			m.display();
			std::cout<<std::endl;
		}
	*///fine salvataggio

	//if(epSquare){// non serve perche' ep[0]=0 (impostato a mano!) 1.2% speedup nel MOVEGEN
		hashKey ^= Board::KEY.ep[epSquare]^Board::KEY.side^Board::KEY.keys[m.from][piece]^Board::KEY.keys[m.to][piece];
	//}
	epSquare=0;								//azzero epsquare
	fiftyMoveCnt++;							//incremento contatore 50 mosse



	boards[piece]^=mbFromTo;	//muovo il pezzo sulle bitboard

	
	if(m.isPawn(piece)){
		fiftyMoveCnt=0;
		pawnKey^=Board::KEY.keys[m.from][piece];
		pawnKey^=Board::KEY.keys[m.to][piece];
		if (m.to-m.from==16)
		{
			epSquare = m.from+8;
			hashKey ^= Board::KEY.ep[m.from+8];
		}
		else if (m.from-m.to==16)
		{
			epSquare = m.from-8;
			hashKey ^= Board::KEY.ep[m.from-8];
		}
	}


	//CATTURE
	if(capturedPiece){																
		fiftyMoveCnt=0;							//resetto contatore 50 mosse 							
		boards[capturedPiece]^=mbTo;	//elimino il pezzo catturato dalla bitboard
		hashKey ^= Board::KEY.keys[m.to][capturedPiece];
		materialKey ^=Board::KEY.keys[pieceCount[capturedPiece]][capturedPiece];
		pieceCount[capturedPiece]--;
		materialKey ^=Board::KEY.keys[pieceCount[capturedPiece]][capturedPiece];
		Material-=Board::PIECESCORE[capturedPiece];
		if(nextMove){// black to play
			if(m.isPawn(capturedPiece)){
				//totalWhitePawns-=Board::PIECESCORE[capturedPiece];
				pawnKey ^= Board::KEY.keys[m.to][capturedPiece];
			}else{
				totalWhitePieces-=Board::PIECESCORE[capturedPiece];
			}
		}else{
			if(m.isPawn(capturedPiece)){
				//totalBlackPawns+=Board::PIECESCORE[capturedPiece];
				pawnKey ^= Board::KEY.keys[m.to][capturedPiece];
			}else{
				totalBlackPieces+=Board::PIECESCORE[capturedPiece];
			}
		}
	}


	square[m.to]=piece;				//muovo il pezzo nello square , contiene gia' il codice della cattura
	square[m.from]=Board::empty;




	//AGGIORNAMENTO  whitepieces/blackpieces bitboard
	boards[Board::whitePieces+nextMove]^=mbFromTo;
	if(capturedPiece)boards[Board::blackPieces-nextMove]^=mbTo;// se e' una cattura elimino il pezzo avversario



	if(m.flags ){
		if(piece==Board::whitePawns)
		{
		
			if(m.flags == Move::fenpassant){
				square[m.to-8]=Board::empty;
				boards[Board::blackPawns]^=BITSET[m.to-8];
				boards[Board::blackPieces]^=BITSET[m.to-8];
				materialKey ^=Board::KEY.keys[pieceCount[Board::blackPawns]][Board::blackPawns];
				pieceCount[Board::blackPawns]--;
				materialKey ^=Board::KEY.keys[pieceCount[Board::blackPawns]][Board::blackPawns];
				hashKey^= Board::KEY.keys[m.to-8][Board::blackPawns];
				pawnKey^= Board::KEY.keys[m.to-8][Board::blackPawns];
				//totalBlackPawns+=Board::PIECESCORE[Board::blackPawns];
				Material-=Board::PIECESCORE[Board::blackPawns];
			}else
			if(m.flags== Move::fpromotion){ // promozione
				square[m.to]=(Board::bitboardIndex)(Board::whiteQueens+m.promotion);
				boards[Board::whitePawns]^=mbTo; // elimino il pedone
				//boards[Board::whitePieces]^=mbTo; // aggiungo il pezzo
				pawnKey ^= Board::KEY.keys[m.to][Board::whitePawns];
				materialKey ^=Board::KEY.keys[pieceCount[Board::whitePawns]][Board::whitePawns];
				pieceCount[Board::whitePawns]--;
				materialKey ^=Board::KEY.keys[pieceCount[Board::whitePawns]][Board::whitePawns];
				materialKey ^=Board::KEY.keys[pieceCount[Board::whiteQueens+m.promotion]][Board::whiteQueens+m.promotion];
				pieceCount[Board::whiteQueens+m.promotion]++;
				materialKey ^=Board::KEY.keys[pieceCount[Board::whiteQueens+m.promotion]][Board::whiteQueens+m.promotion];
				hashKey ^= Board::KEY.keys[m.to][Board::whitePawns]^Board::KEY.keys[m.to][Board::whiteQueens+m.promotion];
				boards[Board::whiteQueens+m.promotion]^=mbTo; // aggiungo il pezzo
				//totalWhitePawns-=Board::PIECESCORE[Board::whitePawns];
				totalWhitePieces+=Board::PIECESCORE[Board::whiteQueens+m.promotion];
				Material-=Board::PIECESCORE[Board::whitePawns];
				Material+=Board::PIECESCORE[Board::whiteQueens+m.promotion];

			}
		}else if(piece==Board::blackPawns){
			if(m.flags == Move::fenpassant){
				square[m.to+8]=Board::empty;
				boards[Board::whitePawns]^=BITSET[m.to+8];
				boards[Board::whitePieces]^=BITSET[m.to+8];
				materialKey ^=Board::KEY.keys[pieceCount[Board::whitePawns]][Board::whitePawns];
				pieceCount[Board::whitePawns]--;
				materialKey ^=Board::KEY.keys[pieceCount[Board::whitePawns]][Board::whitePawns];
				hashKey^= Board::KEY.keys[m.to+8][Board::whitePawns];
				pawnKey^= Board::KEY.keys[m.to+8][Board::whitePawns];
				//totalWhitePawns-=Board::PIECESCORE[Board::whitePawns];
				Material-=Board::PIECESCORE[Board::whitePawns];
			}else if(m.flags== Move::fpromotion){ // promozione
				square[m.to]=(Board::bitboardIndex)(Board::blackQueens+m.promotion);
				boards[Board::blackPawns]^=mbTo; // elimino il pedone
				//boards[Board::blackPieces]^=mbTo; // aggiungo il pezzo
				pawnKey ^= Board::KEY.keys[m.to][Board::blackPawns];

				materialKey ^=Board::KEY.keys[pieceCount[Board::blackPawns]][Board::blackPawns];
				pieceCount[Board::blackPawns]--;
				materialKey ^=Board::KEY.keys[pieceCount[Board::blackPawns]][Board::blackPawns];

				materialKey ^=Board::KEY.keys[pieceCount[Board::blackQueens+m.promotion]][Board::blackQueens+m.promotion];
				pieceCount[Board::blackQueens+m.promotion]++;
				materialKey ^=Board::KEY.keys[pieceCount[Board::blackQueens+m.promotion]][Board::blackQueens+m.promotion];

				hashKey ^= Board::KEY.keys[m.to][Board::blackPawns]^Board::KEY.keys[m.to][Board::blackQueens+m.promotion];
				boards[Board::blackQueens+m.promotion]^=mbTo; // aggiungo il pezzo
				//totalBlackPawns+=Board::PIECESCORE[Board::blackPawns];
				totalBlackPieces-=Board::PIECESCORE[Board::blackQueens+m.promotion];
				Material-=Board::PIECESCORE[Board::blackPawns];
				Material+=Board::PIECESCORE[Board::blackQueens+m.promotion];


			}
		}

	
		if(m.flags == Move::fcastle){
			if(m.to== G1){
				boards[Board::whiteRooks]^=(BITSET[F1]| BITSET[H1]);
				boards[Board::whitePieces]^=(BITSET[F1] | BITSET[H1]);
				square[F1]=Board::whiteRooks;
				square[H1]=Board::empty;
				hashKey^=Board::KEY.keys[F1][Board::whiteRooks]^Board::KEY.keys[H1][Board::whiteRooks] ;
			
			}else if(m.to== C1){
				boards[Board::whiteRooks]^=(BITSET[D1]| BITSET[A1]);
				boards[Board::whitePieces]^=(BITSET[D1]| BITSET[A1]);
				square[D1]=Board::whiteRooks;
				square[A1]=Board::empty;
				hashKey^=Board::KEY.keys[A1][Board::whiteRooks]^Board::KEY.keys[D1][Board::whiteRooks] ;
			}else if(m.to== G8){
				boards[Board::blackRooks]^=(BITSET[F8]| BITSET[H8]);
				boards[Board::blackPieces]^=(BITSET[F8]| BITSET[H8]);
				square[F8]=Board::blackRooks;
				square[H8]=Board::empty;
				hashKey^=Board::KEY.keys[F8][Board::blackRooks]^Board::KEY.keys[H8][Board::blackRooks] ;
			}else if(m.to== C8){
				boards[Board::blackRooks]^=(BITSET[D8]| BITSET[A8]);
				boards[Board::blackPieces]^=(BITSET[D8]| BITSET[A8]);
				square[D8]=Board::blackRooks;
				square[A8]=Board::empty;
				hashKey^=Board::KEY.keys[D8][Board::blackRooks]^Board::KEY.keys[A8][Board::blackRooks] ;
			}
		}
	}

	if(castleRights){
		// aggiornamento castle Rights
		if(m.from==E1){
			if(castleRights & wCastleOO){
				hashKey^=Board::KEY.castlingRight[wCastleOO];
			}
			if(castleRights & wCastleOOO){
				hashKey^=Board::KEY.castlingRight[wCastleOOO];
			}
			castleRights &=~(wCastleOO |wCastleOOO );
		}else
		if(m.from==E8){
			if(castleRights & bCastleOO){
				hashKey^=Board::KEY.castlingRight[bCastleOO];
			}
			if(castleRights & bCastleOOO){
				hashKey^=Board::KEY.castlingRight[bCastleOOO];
			}
			castleRights &=~(bCastleOO |bCastleOOO );
		}
		if(m.from == A1 || m.to == A1 ){
			if(castleRights & wCastleOOO){
				hashKey^=Board::KEY.castlingRight[wCastleOOO];
			}
			castleRights &=~(wCastleOOO);
		}
		if(m.from == H1 || m.to == H1){
			if(castleRights & wCastleOO){
				hashKey^=Board::KEY.castlingRight[wCastleOO];
			}
			castleRights &=~(wCastleOO);
		}
		if(m.from == A8 || m.to == A8){
			if(castleRights & bCastleOOO){
				hashKey^=Board::KEY.castlingRight[bCastleOOO];
			}
			castleRights &=~(bCastleOOO);
		}
		if(m.from == H8 || m.to == H8){
			if(castleRights & bCastleOO){
				hashKey^=Board::KEY.castlingRight[bCastleOO];
			}
			castleRights &=~(bCastleOO);
		}
	}

//	char * tt= (char*)transTT.firstEntry(hashKey);
//	_mm_prefetch(tt, _MM_HINT_T2);
//	_mm_prefetch(tt+64, _MM_HINT_T2); // 64 bytes ahead
	boards[occupiedSquares]=boards[whitePieces]|boards[blackPieces];

	
	nextMove = (Board::eNextMove)(Board::black-nextMove);	//inverto il colore attivo
	getPinnedPieces();
	/*if(gameLineIndex>19)
	{	display();
		board.eval<true>();
	}*/
#ifdef _DEBUG_MAKEMOVE
	checkBoard();
#endif
}

void Board::getPinnedPieces(){
	pinnedPieces=0;
	hiddenCheckers=0;

	unsigned int kingSq=firstOne(boards[Board::whiteKing]);
	bitMap possibleAttackers=0,temp;
	possibleAttackers |=(RANKMASK[kingSq]| FILEMASK[kingSq] ) & (boards[Board::blackQueens]| boards[Board::blackRooks]);
	possibleAttackers |=(DIAGA1H8MASK[kingSq]| DIAGA8H1MASK[kingSq] ) & (boards[Board::blackQueens]| boards[Board::blackBishops]);


	checkers=KNIGHT_ATTACKS[kingSq]& boards[Board::blackKnights];
	checkers|=WHITE_PAWN_ATTACKS[kingSq]& boards[Board::blackPawns];
	//checkers|=KING_ATTACKS[kingSq]& boards[Board::blackKing]; // non serve, i re non possono mai venire a contatto lo previene il MOVEGEN
	/*if(KING_ATTACKS[kingSq]& boards[Board::blackKing]){
		display();

	}*/

	while(possibleAttackers){
		unsigned int att = firstOne(possibleAttackers);
		temp=SQUARES_BETWEEN[kingSq][att]&boards[Board::occupiedSquares];
		if(!temp){
			checkers|=BITSET[att];

		}else if(bitCnt(temp)==1){
			pinnedPieces|=(SQUARES_BETWEEN[kingSq][att]&boards[Board::whitePieces]);
			if(SQUARES_BETWEEN[kingSq][att]&boards[Board::blackPieces]){
				hiddenCheckers|=BITSET[att];
			}
		}
		possibleAttackers &= possibleAttackers-1;

	}

	kingSq=firstOne(boards[Board::blackKing]);
	possibleAttackers=0;
	possibleAttackers |=(RANKMASK[kingSq]| FILEMASK[kingSq] ) & (boards[Board::whiteQueens]| boards[Board::whiteRooks]);
	possibleAttackers |=(DIAGA1H8MASK[kingSq]| DIAGA8H1MASK[kingSq] ) & (boards[Board::whiteQueens]| boards[Board::whiteBishops]);

	checkers|=KNIGHT_ATTACKS[kingSq]& boards[Board::whiteKnights];
	checkers|=BLACK_PAWN_ATTACKS[kingSq]& boards[Board::whitePawns];
	//checkers|=KING_ATTACKS[kingSq]& boards[Board::whiteKing];// non serve, i re non possono mai venire a contatto lo previene il MOVEGEN
	/*if(KING_ATTACKS[kingSq]& boards[Board::whiteKing]){
		display();

	}*/

	while(possibleAttackers){
		unsigned int att = firstOne(possibleAttackers);
		temp=SQUARES_BETWEEN[kingSq][att]&boards[Board::occupiedSquares];
		if(!temp){
			checkers|=BITSET[att];
		}else if(bitCnt(temp)==1){
			pinnedPieces|=(SQUARES_BETWEEN[kingSq][att]&boards[Board::blackPieces]);
			if(SQUARES_BETWEEN[kingSq][att]&boards[Board::whitePieces]){
				hiddenCheckers|=BITSET[att];
			}
		}
		possibleAttackers &= possibleAttackers-1;

	}



}
